﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace System.Peppers.UI
{
    /// <summary>
    /// Class for holding (x,y) coordinates, similar to System.Drawing.Point
    /// </summary>
    [Serializable, StructLayout(LayoutKind.Sequential)]
    public struct Point
    {
        /// <summary>
        /// Reference to an empty point
        /// </summary>
        public static readonly Point Empty = new Point();
        private int x;
        private int y;

        /// <summary>
        /// Ctor with 2 coordinates
        /// </summary>
        /// <param name="x">x pos</param>
        /// <param name="y">y pos</param>
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        /// <summary>
        /// Ctor taking a 32 bit number and converting to a Point
        /// </summary>
        /// <param name="dw">DWORD</param>
        public Point(int dw)
        {
            this.x = (short)LOWORD(dw);
            this.y = (short)HIWORD(dw);
        }

        /// <summary>
        /// Returns if x and y are both zero
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                return ((this.x == 0) && (this.y == 0));
            }
        }

        /// <summary>
        /// X coordinate
        /// </summary>
        public int X
        {
            get
            {
                return this.x;
            }
            set
            {
                this.x = value;
            }
        }

        /// <summary>
        /// Y coordinate
        /// </summary>
        public int Y
        {
            get
            {
                return this.y;
            }
            set
            {
                this.y = value;
            }
        }

        /// <summary>
        /// Handles addition of 2 points
        /// </summary>
        /// <param name="pt1">point 1</param>
        /// <param name="pt2">point 2</param>
        /// <returns>Added point</returns>
        public static Point operator +(Point pt1, Point pt2)
        {
            return new Point(pt1.x + pt2.x, pt1.y + pt2.y);
        }

        /// <summary>
        /// Handles subtraction of 2 points
        /// </summary>
        /// <param name="pt1">point 1</param>
        /// <param name="pt2">point 2</param>
        /// <returns>Subtracted point</returns>
        public static Point operator -(Point pt1, Point pt2)
        {
            return new Point(pt1.x - pt2.x, pt1.y - pt2.y);
        }

        /// <summary>
        /// Checks equality on 2 points
        /// </summary>
        /// <param name="left">point 1</param>
        /// <param name="right">point 2</param>
        /// <returns>If equal</returns>
        public static bool operator ==(Point left, Point right)
        {
            return ((left.X == right.X) && (left.Y == right.Y));
        }

        /// <summary>
        /// Checks if 2 points are not equal
        /// </summary>
        /// <param name="left">point 1</param>
        /// <param name="right">point 1</param>
        /// <returns>If not equal</returns>
        public static bool operator !=(Point left, Point right)
        {
            return !(left == right);
        }
        
        /// <summary>
        /// Checks equality
        /// </summary>
        /// <param name="obj">Point to compare</param>
        /// <returns>X == X and Y == Y</returns>
        public override bool Equals(object obj)
        {
            if (!(obj is Point))
            {
                return false;
            }
            Point point = (Point)obj;
            return ((point.X == this.X) && (point.Y == this.Y));
        }

        /// <summary>
        /// Hash code of point
        /// </summary>
        /// <returns>x ^ y</returns>
        public override int GetHashCode()
        {
            return (this.x ^ this.y);
        }

        /// <summary>
        /// Offsets point by x and y distance
        /// </summary>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        public void Offset(int dx, int dy)
        {
            this.X += dx;
            this.Y += dy;
        }

        private static int HIWORD(int n)
        {
            return ((n >> 0x10) & 0xffff);
        }

        private static int LOWORD(int n)
        {
            return (n & 0xffff);
        }
    }
}
